#include "SubclassOfType.h"

#include "PlaceholderType.h"

#include "SubclassOf.h"

#include "FunctionList.h"

#include "NativeFunction.h"

#include "TypeInstance.h"

CString CSubclassOfType::PlaceholderName = U"T";

CSubclassOfType::CSubclassOfType()
    :CType(
        STypeID(CType::GetEngineCoreNamespace()+U"::TSubclassOf",true)
        ,ETypeFlags::Generic | ETypeFlags::SubclassOf
    )
{
    GenericInfo=new CGenericInfo();

    CType *PlaceholderType= new CPlaceholderType(
        STypeID(GetFullName()+U"::"+PlaceholderName,true)
        ,ETypeFlags::None
        ,0
        ,this);
    GenericInfo->PlaceholderTypes.Add(PlaceholderType);

    Functions=new CFunctionList(this);
    {
        //add ToString virtual function
        CNativeFunction *NativeFunction = new CNativeFunction(
            Functions,
            U"ToString",
            CGetType<CString>()(),
            {},
            {}
            );

        Functions->VirtualFunctionTable.Add(NativeFunction->ID);

    }
}

uint8_t RegisterSubclassOfTypeCaller=(CSubclassOfType::GetStaticType(),0);

CSubclassOfType *CSubclassOfType::GetStaticType()
{
    static CSubclassOfType *StaticType=new CSubclassOfType();
    return StaticType;
}

CString CSubclassOfType::CreateTypeName(const CString &ElementType)
{
    return STypeID::CreateTemplateTypeName(
       CSubclassOfType::GetStaticType()->GetFullName()
        ,{ElementType}
        ,{PlaceholderName}
        );
}
 
STypeID CSubclassOfType::CreateID(const SDecoratedType & ElementType)
{
    return STypeID(
        CSubclassOfType::GetStaticType()->ID
        ,{ElementType.BasicType->ID}
        ,{PlaceholderName}
        );
}

class CSubclassInstance : public ITypeInstance
{
protected:
    CSubclassOfGeneralized *Type;
public:
    CSubclassInstance(CSubclassOfGeneralized *InType)
        :Type(InType)
    {
    }

    virtual uint32_t GetSize() override
    {
        return sizeof(STypeOfBase);
    }

    virtual void InitInstance(void *Memory)
    {
        new (Memory) STypeOfBase();
    }

    virtual void CloneInstance(const void *Instance, void *Memory)
    {
        new (Memory) STypeOfBase(*(const STypeOfBase *)Instance);
    }

    virtual void EraseInstance(void *InValue)
    {
        // do nothing
    }

    virtual CString ToDebugString(const void *InInstance) const override
    {
        return U"SubclassOf<" + Type->GetElementType()->GetFullName() + U">";
    }

    virtual uint64_t GetInstanceHash(const void *InInstance) const override
    {
        return (uint64_t)((( STypeOfBase const*)InInstance)->Type);
    }

    virtual bool IsInstanceEqual(const void *A, const void *B) const override
    {
        return ((STypeOfBase const*)A)->Type == ((STypeOfBase const*)B)->Type;
    }

    virtual void *CreateInstance() override
    {
        return new STypeOfBase();
    }

    virtual void ResetAllocated(void *Memory) override
    {
        ((STypeOfBase*)Memory)->Type=nullptr;
    }

};


CSubclassOfGeneralized::CSubclassOfGeneralized(CType *InElementType)
    :CType(
        CSubclassOfType::CreateID(InElementType)
        , ETypeFlags::Generalized | ETypeFlags::SubclassOf
          | (InElementType->IsScriptType()? ETypeFlags::Script : ETypeFlags::None)
          | (InElementType->CanUseInScript()? ETypeFlags::None : ETypeFlags::CantUseInScript)
    )
{
    assert(InElementType->IsClassType() && "SubclassOf<T> T must be class type");

    Instancable=new CSubclassInstance(this);

    GeneralizedInfo=new CGeneralizedInfo();
    GeneralizedInfo->TemplateType=CSubclassOfType::GetStaticType();
    GeneralizedInfo->TypeArguments.Add(InElementType);

    GeneralizedInfo->TemplateType->GenericInfo->GeneralizedTypes.Add(this);
    reinterpret_cast<CGenericInfo *>(GeneralizedInfo->TemplateType->GenericInfo)
        ->GenericMap.Add(TVector<CType*>({InElementType}), this);

    Functions=new CFunctionList(this);
    {
        //add ToString virtual function
        CNativeFunction *NativeFunction = new CNativeFunction(
            Functions
            ,U"ToString"
            , CGetType<CString>()()
            ,{}
            ,{}
            );

        NativeFunction->SetCppBody([this](const TVector<CAny> &InArgs)->CAny
        {
            STypeOfBase* TypeOfBase= *reinterpret_cast<STypeOfBase**>(InArgs[0].GetPointer());
            return TypeOfBase->Type->GetFullName();
        });

        Functions->VirtualFunctionTable.Add(NativeFunction->ID);

    }

}

CClass *CSubclassOfGeneralized::GetElementType()
{
    return reinterpret_cast<CClass *>(GeneralizedInfo->TypeArguments[0]);
}